package com.tjvib.util;

import static com.tjvib.Constants.SUCCESS;

import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;

import androidx.core.content.FileProvider;

import com.tjvib.TJVIBAPP;
import com.tjvib.bean.DataSet;
import com.tjvib.bean.DataSetInfo;
import com.tjvib.common.Callback;
import com.tjvib.sensor.LocalSensorData;
import com.tjvib.sensor.LpmsB2SensorData;
import com.tjvib.sensor.ZeroLabSensorData;
import com.tjvib.util.Manager.UserManager;
import com.tjvib.util.retrofit.BaseResponse;
import com.tjvib.util.retrofit.RetrofitHelper;
import com.tjvib.widget.AndroidScheduler;

import org.json.JSONException;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Scanner;

import io.reactivex.Observer;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
import okhttp3.MediaType;
import okhttp3.RequestBody;

public class FileUtil {
    private static SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS");

    public static void importFromCsv(Context context, Uri uri, String name, Callback callback) {
        new Thread(() -> {
            InputStream fileInputStream;
            Scanner in;
            try {
                fileInputStream = context.getContentResolver().openInputStream(uri);
                in = new Scanner(fileInputStream);

                String type;
                String[] columnTitles = in.nextLine().split(",");
                if (columnTitles.length == 27) {
                    type = "lpmsb2";
                } else if (columnTitles.length == 18) {
                    type = "local";
                } else {
                    callback.onError("未知数据类型：" + columnTitles.length, null);
                    return;
                }

                DataSetInfo dataSetInfo = new DataSetInfo(-1, name, type, false,
                        new Timestamp(System.currentTimeMillis()).toString());
                DataSet dataSet;
                if (type.equals("lpmsb2")) {
                    List<LpmsB2SensorData> content = new ArrayList<>();
                    while (in.hasNextLine()) {
                        String[] splits = in.nextLine().split(",");
                        if (splits.length != 27) {
                            continue;
                        }

                        LpmsB2SensorData item = new LpmsB2SensorData();
                        Date date = format.parse(splits[1]);
                        if (date == null) {
                            continue;
                        }
                        item.imuId = Integer.parseInt(splits[0]);
                        item.localtime = date.getTime();
                        item.timestamp = Float.parseFloat(splits[2]);
                        item.frameNumber = Integer.parseInt(splits[3]);
                        item.acc[0] = Float.parseFloat(splits[4]);
                        item.acc[1] = Float.parseFloat(splits[5]);
                        item.acc[2] = Float.parseFloat(splits[6]);
                        item.gyr[0] = Float.parseFloat(splits[7]);
                        item.gyr[1] = Float.parseFloat(splits[8]);
                        item.gyr[2] = Float.parseFloat(splits[9]);
                        item.mag[0] = Float.parseFloat(splits[10]);
                        item.mag[1] = Float.parseFloat(splits[11]);
                        item.mag[2] = Float.parseFloat(splits[12]);
                        item.euler[0] = Float.parseFloat(splits[13]);
                        item.euler[1] = Float.parseFloat(splits[14]);
                        item.euler[2] = Float.parseFloat(splits[15]);
                        item.quat[0] = Float.parseFloat(splits[16]);
                        item.quat[1] = Float.parseFloat(splits[17]);
                        item.quat[2] = Float.parseFloat(splits[18]);
                        item.quat[3] = Float.parseFloat(splits[19]);
                        item.linAcc[0] = Float.parseFloat(splits[20]);
                        item.linAcc[1] = Float.parseFloat(splits[21]);
                        item.linAcc[2] = Float.parseFloat(splits[22]);
                        item.pressure = Float.parseFloat(splits[23]);
                        item.altitude = Float.parseFloat(splits[24]);
                        item.temperature = Float.parseFloat(splits[25]);
                        item.batteryLevel = Float.parseFloat(splits[26]);
                        content.add(item);
                    }
                    dataSet = new DataSet<>(dataSetInfo, content);
                } else {
                    List<LocalSensorData> content = new ArrayList<>();
                    while (in.hasNextLine()) {
                        String[] splits = in.nextLine().split(",");
                        if (splits.length != 18) {
                            continue;
                        }

                        LocalSensorData item = new LocalSensorData();
                        Date date = format.parse(splits[0]);
                        if (date == null) {
                            continue;
                        }
                        item.localtime = date.getTime();
                        item.timestamp = Float.parseFloat(splits[1]);
                        item.acc[0] = Float.parseFloat(splits[2]);
                        item.acc[1] = Float.parseFloat(splits[3]);
                        item.acc[2] = Float.parseFloat(splits[4]);
                        item.gyr[0] = Float.parseFloat(splits[5]);
                        item.gyr[1] = Float.parseFloat(splits[6]);
                        item.gyr[2] = Float.parseFloat(splits[7]);
                        item.mag[0] = Float.parseFloat(splits[8]);
                        item.mag[1] = Float.parseFloat(splits[9]);
                        item.mag[2] = Float.parseFloat(splits[10]);
                        item.quat[0] = Float.parseFloat(splits[11]);
                        item.quat[1] = Float.parseFloat(splits[12]);
                        item.quat[2] = Float.parseFloat(splits[13]);
                        item.quat[3] = Float.parseFloat(splits[14]);
                        item.linAcc[0] = Float.parseFloat(splits[15]);
                        item.linAcc[1] = Float.parseFloat(splits[16]);
                        item.linAcc[2] = Float.parseFloat(splits[17]);
                        content.add(item);
                    }
                    dataSet = new DataSet<>(dataSetInfo, content);
                }

                final String fType = type;
                String dataSetStr = null;
                try {
                    dataSetStr = dataSet.toJsonString();
                } catch (JSONException e) {
                    e.printStackTrace();
                }
                RequestBody requestBody = RequestBody.create(MediaType.parse("application/json"), dataSetStr);
                if (fType.equals("lpmsb2")) {
                    RetrofitHelper.getInstance().create()
                            .collectLpmsB2("" + UserManager.getInstance().getUserId(), requestBody)
                            .subscribeOn(Schedulers.io())
                            .observeOn(AndroidScheduler.mainThread())
                            .subscribe(new Observer<BaseResponse>() {
                                @Override
                                public void onSubscribe(Disposable d) {

                                }

                                @Override
                                public void onNext(BaseResponse value) {
                                    if (value.getCode() == SUCCESS) {
                                        callback.onSuccess(value.getMessage(), null);
                                        UserManager.getInstance().setInfo(String.valueOf(value.getData()));
                                    } else {
                                        callback.onFail(value.getMessage(), null);
                                    }
                                }

                                @Override
                                public void onError(Throwable e) {
                                    callback.onFail(e.getMessage(), null);
                                    LogUtil.e(e.getMessage());
                                }

                                @Override
                                public void onComplete() {

                                }
                            });
                } else {
                    RetrofitHelper.getInstance().create()
                            .collectLocal("" + UserManager.getInstance().getUserId(), requestBody)
                            .subscribe(new Observer<BaseResponse>() {
                                @Override
                                public void onSubscribe(Disposable d) {

                                }

                                @Override
                                public void onNext(BaseResponse value) {
                                    if (value.getCode() == SUCCESS) {
                                        callback.onSuccess(value.getMessage(), null);
                                        UserManager.getInstance().setInfo(String.valueOf(value.getData()));
                                    } else {
                                        callback.onFail(value.getMessage(), null);
                                    }
                                }

                                @Override
                                public void onError(Throwable e) {
                                    callback.onFail(e.getMessage(), null);
                                    LogUtil.e(e.getMessage());
                                }

                                @Override
                                public void onComplete() {

                                }
                            });
                }
            } catch (Exception e) {
                e.printStackTrace();
                callback.onError(e.getMessage(), null);
            }
        }).start();
    }

    public static void exportToCsv(Context context, DataSet dataset, Callback callback) {
        new Thread(() -> {
            try {
                String fileName = dataset.getInfo().getName() + ".csv";
                File exportFolder = new File(context.getExternalFilesDir(null) + "/datasets");
                if (!exportFolder.exists()) {
                    if (!exportFolder.mkdir()) {
                        callback.onError("创建文件夹失败", null);
                        return;
                    }
                }

                File file = new File(exportFolder, fileName);
                if (file.exists()) {
                    file.delete();
                }
                if (!file.createNewFile()) {
                    callback.onError("创建文件失败", null);
                    return;
                }

                FileOutputStream fos = new FileOutputStream(file);
                OutputStreamWriter fileWriter = new OutputStreamWriter(fos);

                if (dataset.getInfo().getType().equals("local")) {
                    fileWriter.append("LocalTime, TimeStamp (s)," +
                            " AccX (g), AccY (g), AccZ (g)," +
                            " GyroX (deg/s), GyroY (deg/s), GyroZ (deg/s)," +
                            " MagX (uT), MagY (uT), MagZ (uT)," +
                            " QuatW, QuatX, QuatY, QuatZ," +
                            " LinAccX(g) , LinAccY(g) , LinAccZ(g)\n");
                    List<LocalSensorData> content = dataset.getContent();
                    for (LocalSensorData item : content) {
                        fileWriter.append(format.format(new Date(item.localtime))).append(",").append(String.valueOf(item.timestamp)).append(",")
                                .append(String.valueOf(item.acc[0])).append(",").append(String.valueOf(item.acc[1])).append(",").append(String.valueOf(item.acc[2])).append(",")
                                .append(String.valueOf(item.gyr[0])).append(",").append(String.valueOf(item.gyr[1])).append(",").append(String.valueOf(item.gyr[2])).append(",")
                                .append(String.valueOf(item.mag[0])).append(",").append(String.valueOf(item.mag[1])).append(",").append(String.valueOf(item.mag[2])).append(",")
                                .append(String.valueOf(item.quat[0])).append(",").append(String.valueOf(item.quat[1])).append(",").append(String.valueOf(item.quat[2])).append(",").append(String.valueOf(item.quat[3])).append(",")
                                .append(String.valueOf(item.linAcc[0])).append(",").append(String.valueOf(item.linAcc[1])).append(",").append(String.valueOf(item.linAcc[2])).append("\n");
                    }
                } else if (dataset.getInfo().getType().equals("lpmsb2")) {
                    fileWriter.append("SensorId,LocalTime,TimeStamp (s),FrameNumber," +
                            " AccX (g), AccY (g), AccZ (g)," +
                            " GyroX (deg/s), GyroY (deg/s), GyroZ (deg/s)," +
                            " MagX (uT), MagY (uT), MagZ (uT)," +
                            " EulerX(deg) , EulerY(deg) , EulerZ(deg) ," +
                            " QuatW, QuatX, QuatY, QuatZ," +
                            " LinAccX(g) , LinAccY(g) , LinAccZ(g) ," +
                            " Pressure(kPa), Altitude(m), Temperature(degC), BatteryLevel(%)\n");
                    List<LpmsB2SensorData> content = dataset.getContent();
                    for (LpmsB2SensorData item : content) {
                        fileWriter.append(String.valueOf(item.imuId)).append(",").append(format.format(new Date(item.localtime))).append(",").append(String.valueOf(item.timestamp)).append(",").append(String.valueOf(item.frameNumber)).append(",")
                                .append(String.valueOf(item.acc[0])).append(",").append(String.valueOf(item.acc[1])).append(",").append(String.valueOf(item.acc[2])).append(",")
                                .append(String.valueOf(item.gyr[0])).append(",").append(String.valueOf(item.gyr[1])).append(",").append(String.valueOf(item.gyr[2])).append(",")
                                .append(String.valueOf(item.mag[0])).append(",").append(String.valueOf(item.mag[1])).append(",").append(String.valueOf(item.mag[2])).append(",")
                                .append(String.valueOf(item.euler[0])).append(",").append(String.valueOf(item.euler[1])).append(",").append(String.valueOf(item.euler[2])).append(",")
                                .append(String.valueOf(item.quat[0])).append(",").append(String.valueOf(item.quat[1])).append(",").append(String.valueOf(item.quat[2])).append(",").append(String.valueOf(item.quat[3])).append(",")
                                .append(String.valueOf(item.linAcc[0])).append(",").append(String.valueOf(item.linAcc[1])).append(",").append(String.valueOf(item.linAcc[2])).append(",")
                                .append(String.valueOf(item.pressure)).append(",").append(String.valueOf(item.altitude)).append(",").append(String.valueOf(item.temperature)).append(",").append(String.valueOf(item.batteryLevel)).append("\n");
                    }
                }

                fileWriter.close();
                fos.close();
                callback.onSuccess(null, null);
            } catch (Exception e) {
                e.printStackTrace();
                callback.onFail(e.getMessage(), null);
            }
        }).start();

    }

    public static boolean isCsvExists(Context context, String dataSetName) {
        return new File(context.getExternalFilesDir(null) + "/datasets/" + dataSetName + ".csv").exists();
    }

    public static OutputStreamWriter getDataSetTempFileWriter(Context context) {
        deleteDataSetTempFile(context);
        try {
            File tmpFile = new File(context.getFilesDir(), "tmpdata");
            if (!tmpFile.createNewFile()) {
                LogUtil.e("tmpFile.createNewFile()=" + tmpFile.createNewFile());
                return null;
            }

            FileOutputStream fos = new FileOutputStream(tmpFile);
            return new OutputStreamWriter(fos);
        } catch (Exception e) {
            e.printStackTrace();
            LogUtil.e("e=" + e.getMessage());
            return null;
        }
    }

    public static boolean deleteDataSetTempFile(Context context) {
        File tmpFile = new File(context.getFilesDir(), "tmpdata");
        LogUtil.e("context.getFilesDir()="+context.getFilesDir());
        return tmpFile.delete();
    }

    public static boolean isDataSetTempFileExists(Context context) {
        File tmpFile = new File(context.getFilesDir(), "tmpdata");
        try {
            FileInputStream fis = new FileInputStream(tmpFile);
            InputStreamReader fileReader = new InputStreamReader(fis);
            BufferedReader bufferedReader = new BufferedReader(fileReader);
            bufferedReader.readLine();
            int cnt = 0;
            while (bufferedReader.readLine() != null) cnt++;
            if(cnt < 3) return false;
            else return tmpFile.exists();
        } catch (Exception e) {
            LogUtil.e(e.getMessage());
            e.printStackTrace();
            return false;
        }
    }

    public static String getTempDataSetType(Context context) {
        File tmpFile = new File(context.getFilesDir(), "tmpdata");

        try {
            FileInputStream fis = new FileInputStream(tmpFile);
            InputStreamReader fileReader = new InputStreamReader(fis);
            BufferedReader bufferedReader = new BufferedReader(fileReader);
            String type = bufferedReader.readLine();

            bufferedReader.close();
            fileReader.close();
            fis.close();

            return type;
        } catch (Exception e) {
            e.printStackTrace();
            return "";
        }
    }

    public static DataSet getTempDataSet(Context context, String dataSetName) {
        File tmpFile = new File(context.getFilesDir(), "tmpdata");
        DataSet result = null;

        try {
            FileInputStream fis = new FileInputStream(tmpFile);
            InputStreamReader fileReader = new InputStreamReader(fis);
            BufferedReader bufferedReader = new BufferedReader(fileReader);
            String type = bufferedReader.readLine();
            String line = null;
            if (type.equals("local")) {
                List<LocalSensorData > content = new ArrayList<>();
                while ((line = bufferedReader.readLine()) != null) {
                    String[] splits = line.split(",");
                    if (splits.length != 18) {
                        continue;
                    }

                    LocalSensorData item = new LocalSensorData();
                    item.localtime = Long.parseLong(splits[0]);
                    item.timestamp = Float.parseFloat(splits[1]);
                    item.acc[0] = Float.parseFloat(splits[2]);
                    item.acc[1] = Float.parseFloat(splits[3]);
                    item.acc[2] = Float.parseFloat(splits[4]);
                    item.gyr[0] = Float.parseFloat(splits[5]);
                    item.gyr[1] = Float.parseFloat(splits[6]);
                    item.gyr[2] = Float.parseFloat(splits[7]);
                    item.mag[0] = Float.parseFloat(splits[8]);
                    item.mag[1] = Float.parseFloat(splits[9]);
                    item.mag[2] = Float.parseFloat(splits[10]);
                    item.quat[0] = Float.parseFloat(splits[11]);
                    item.quat[1] = Float.parseFloat(splits[12]);
                    item.quat[2] = Float.parseFloat(splits[13]);
                    item.quat[3] = Float.parseFloat(splits[14]);
                    item.linAcc[0] = Float.parseFloat(splits[15]);
                    item.linAcc[1] = Float.parseFloat(splits[16]);
                    item.linAcc[2] = Float.parseFloat(splits[17]);
                    content.add(item);
                }

                DataSetInfo info = new DataSetInfo(-1, dataSetName, type, false,
                        new Timestamp(System.currentTimeMillis()).toString());
                result = new DataSet<>(info, content);
            } else if (type.equals("lpmsb2")) {
                List<LpmsB2SensorData > content = new ArrayList<>();
                while ((line = bufferedReader.readLine()) != null) {
                    String[] splits = line.split(",");
                    if (splits.length != 27) {
                        continue;
                    }

                    LpmsB2SensorData item = new LpmsB2SensorData();
                    item.imuId = Integer.parseInt(splits[0]);
                    item.localtime = Long.parseLong(splits[1]);
                    item.timestamp = Float.parseFloat(splits[2]);
                    item.frameNumber = Integer.parseInt(splits[3]);
                    item.acc[0] = Float.parseFloat(splits[4]);
                    item.acc[1] = Float.parseFloat(splits[5]);
                    item.acc[2] = Float.parseFloat(splits[6]);
                    item.gyr[0] = Float.parseFloat(splits[7]);
                    item.gyr[1] = Float.parseFloat(splits[8]);
                    item.gyr[2] = Float.parseFloat(splits[9]);
                    item.mag[0] = Float.parseFloat(splits[10]);
                    item.mag[1] = Float.parseFloat(splits[11]);
                    item.mag[2] = Float.parseFloat(splits[12]);
                    item.euler[0] = Float.parseFloat(splits[13]);
                    item.euler[1] = Float.parseFloat(splits[14]);
                    item.euler[2] = Float.parseFloat(splits[15]);
                    item.quat[0] = Float.parseFloat(splits[16]);
                    item.quat[1] = Float.parseFloat(splits[17]);
                    item.quat[2] = Float.parseFloat(splits[18]);
                    item.quat[3] = Float.parseFloat(splits[19]);
                    item.linAcc[0] = Float.parseFloat(splits[20]);
                    item.linAcc[1] = Float.parseFloat(splits[21]);
                    item.linAcc[2] = Float.parseFloat(splits[22]);
                    item.pressure = Float.parseFloat(splits[23]);
                    item.altitude = Float.parseFloat(splits[24]);
                    item.temperature = Float.parseFloat(splits[25]);
                    item.batteryLevel = Float.parseFloat(splits[26]);
                    content.add(item);
                }

                DataSetInfo info = new DataSetInfo(-1, dataSetName, type, false,
                        new Timestamp(System.currentTimeMillis()).toString());
                result = new DataSet<>(info, content);
            }

            bufferedReader.close();
            fileReader.close();
            fis.close();

            return result;
        } catch (Exception e) {
            LogUtil.e(e.getMessage());
            e.printStackTrace();
            return null;
        }
    }

    public static void openDataSetFile(Context context, String dataSetName, Callback callback) {
        String path = context.getExternalFilesDir(null) + "/datasets/" + dataSetName + ".csv";
        openFileIntent(context, path, "csv", callback);
    }

    public static void shareDataSetFile(Activity activity, String dataSetName) {
        String path = activity.getExternalFilesDir(null) + "/datasets/" + dataSetName + ".csv";
        File file = new File(path);
        Uri uri = FileProvider.getUriForFile(activity, "com.tjvib.fileprovider", file);

        Intent shareIntent = new Intent(Intent.ACTION_SEND);
        shareIntent.setType("text/plain");
        shareIntent.putExtra(Intent.EXTRA_STREAM, uri);
        shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
        activity.startActivity(Intent.createChooser(shareIntent, "分享数据集"));
    }

    public static void shareReportFile(Activity activity, String reportName) {
        String path = activity.getExternalFilesDir(null) + "/" + reportName;
        File file = new File(path);
        Uri uri = FileProvider.getUriForFile(activity, "com.tjvib.fileprovider", file);

        Intent shareIntent = new Intent(Intent.ACTION_SEND);
        shareIntent.setType("text/plain");
        shareIntent.putExtra(Intent.EXTRA_STREAM, uri);
        shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
        activity.startActivity(Intent.createChooser(shareIntent, "分享报告"));
    }

    private static void openFileIntent(Context context, String path, String type, Callback callback) {
        if (path != null && !path.isEmpty()) {
            Uri uri = FileProvider.getUriForFile(context, "com.tjvib.fileprovider", new File(path));
            Intent intent = new Intent(Intent.ACTION_VIEW);
            if (type.equals("doc") || type.equals("docx")) {
                intent.setDataAndType(uri, "application/msword");
            } else if (type.equals("xls") || type.equals("csv")) {
                intent.setDataAndType(uri, "text/csv");
            } else if (type.equals("ppt")) {
                intent.setDataAndType(uri, "application/vnd.ms-powerpoint");
            }
            intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
            context.startActivity(intent);
        }
    }

    public static void openReportFile(Context context, String reportName, Callback callback){
        File downloadDirectory = TJVIBAPP.getApplication().getExternalFilesDir(null);
        String path = downloadDirectory + "/" + reportName;
        openFileIntent(context, path, "docx", callback);
    }

    public static void deleteReportFile(String reportName){
        File downloadDirectory = TJVIBAPP.getApplication().getExternalFilesDir(null);
        File file = new File(downloadDirectory + "/" + reportName);
        file.delete();
    }




    private static final String RAW_DATA_DIR = "raw_data";
    private static final String RAW_DATA_FILE = "raw_data.bin";

    /**
     * 将原始字节数据写入文件
     * @param context 上下文
     * @param data    要写入的字节数组
     */
    public static void writeRawData(Context context, byte[] data) {
        FileOutputStream fos = null;
        try {
            // 获取内部存储的目录
            File dir = new File(context.getFilesDir(), RAW_DATA_DIR);
            if (!dir.exists()) {
                dir.mkdirs();
            }
            File rawDataFile = new File(dir, RAW_DATA_FILE);
            fos = new FileOutputStream(rawDataFile, true);
            fos.write(data);
            fos.flush();
        } catch (IOException e) {
            e.printStackTrace();
            LogUtil.e("写入原始数据失败: " + e.getMessage());
        } finally {
            if (fos != null) {
                try {
                    fos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                    LogUtil.e("关闭文件输出流失败: " + e.getMessage());
                }
            }
        }
    }

    /**
     * 获取原始数据文件的路径
     * @param context 上下文
     * @return 原始数据文件
     */
    public static File getRawDataFile(Context context) {
        File dir = new File(context.getFilesDir(), RAW_DATA_DIR);
        if (!dir.exists()) {
            dir.mkdirs();
        }
        return new File(dir, RAW_DATA_FILE);
    }

    /**
     * 删除原始数据文件
     * @param context 上下文
     */
    public static void deleteRawDataFile(Context context) {
        File rawDataFile = getRawDataFile(context);
        if (rawDataFile.exists()) {
            rawDataFile.delete();
        }
    }
}
